/*
===========================================================================
This source file is part of DyLab (Dynamic Laboratory)
For the latest info, see http://dylab.googlecode.com

Copyright (c) 2006-2008 Lukas Krejci
(krejci.lukas@volny.cz)

This file is part of DyLab.

    DyLab is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    DyLab is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with DyLab.  If not, see <http://www.gnu.org/licenses/>.
===========================================================================
*/

#ifndef __dylab_GuardedObject_h__
#define __dylab_GuardedObject_h__

//===========================================================================================
#include <dylab/utils/thread/GuardedData.h>

//===========================================================================================
namespace dylab {

    /** 
     * Thread-safety, assignments-safety (cant't assign NULL value), deletion on destruction or re-assignments.
     */    
    template < 
        typename _ClassT
    >
    class GuardedObject 
        : public GuardedData <_ClassT *>
    {
    public:
        typedef typename GuardedObject<_ClassT> GuardedObjectT;

    public:
        ///
        class ScopedShLock
        {
        protected:
            /// the object does not own this pointer
            const GuardedObjectT * mGuardedObject;

            /// the naked pointer to the object
            const _ClassT * mObjectPtr;

        public:
            ScopedShLock(const GuardedObjectT * guardedObjPtr)
                : mGuardedObject(guardedObjPtr)
                    { DYLAB_ASSERT(mGuardedObject != NULL); mObjectPtr = mGuardedObject->shLock(); }

            virtual ~ScopedShLock()
                    { mGuardedObject->shUnlock(); }

            const _ClassT * operator -> () const
                    { return mObjectPtr; }    

            operator const _ClassT * () const
                    { return mObjectPtr; }    
        };
        //-----------------------------------------------------------------------------
    public:
        ///
        template < 
            class _InternalClassT
        >
        class ScopedShLock_InternalObj
            : public ScopedShLock
        {
        protected:
            ///
            const _InternalClassT * mInternalObjPtr;

        public:
            ScopedShLock_InternalObj(const GuardedObjectT * _guard)
                : ScopedShLock(_guard)
                    { }

            operator const _InternalClassT * () const
                    { 
                        DYLAB_ASSERT(mInternalObjPtr != NULL);
                        return mInternalObjPtr; 
                    }

            const _InternalClassT * operator-> () const
                    { 
                        DYLAB_ASSERT(mInternalObjPtr != NULL);
                        return mInternalObjPtr; 
                    }
        };
        //-----------------------------------------------------------------------------

    public:
        ///
        class ScopedExLock
        {
        protected:
            /// the object does not own this pointer
            GuardedObjectT * mGuardedObject;

            /// the naked pointer to the object
            _ClassT * mObjectPtr;

        public:
            ScopedExLock(GuardedObjectT * _guardedObjPtr)
                : mGuardedObject(_guardedObjPtr)
                    { DYLAB_ASSERT(mGuardedObject != NULL); mObjectPtr = mGuardedObject->exLock(); }

            virtual ~ScopedExLock()
                    { mGuardedObject->exUnlock(); }

            _ClassT * operator -> ()
                    { return mObjectPtr; }    

            operator _ClassT * ()
                    { return mObjectPtr; }    
        };
        //-----------------------------------------------------------------------------
    public:
        ///
        template < 
            class _InternalClassT
        >
        class ScopedExLock_InternalObj
            : public ScopedExLock
        {
        protected:
            ///
            _InternalClassT * mInternalObjPtr;

        public:
            ScopedExLock_InternalObj(GuardedObjectT * _guard)
                : ScopedExLock(_guard)
                    { }

            operator _InternalClassT * ()
                    { 
                        DYLAB_ASSERT(mInternalObjPtr != NULL);
                        return mInternalObjPtr; 
                    }

            _InternalClassT * operator-> ()
                    { 
                        DYLAB_ASSERT(mInternalObjPtr != NULL);
                        return mInternalObjPtr; 
                    }
        };
        //-----------------------------------------------------------------------------

    public:

        //===========================================================================================
        // Constructors & destructor
        //===========================================================================================

        /**
         *
         */
        GuardedObject()
            : GuardedData<_ClassT *> (NULL)
                { }

        /**
         *
         * @remarks  pointer has to be created with operator new (not new []!).
         */
        GuardedObject(_ClassT * object)
            : GuardedData<_ClassT *>(object)
                { }

        /**
         *
         */
        virtual ~GuardedObject()
                { destroy(); }

        //===========================================================================================
        // Object pointer access methods
        //===========================================================================================

        /**
         *
         */
        void assign(_ClassT * _object)
                { DYLAB_ASSERT(_object != NULL);
					_ScopedExLock assignLock(this);
                    if (assignLock.getData() != NULL)
                        delete assignLock.getData();
                    assignLock.setData(_object);
                }

        /**
         *
         */
        void destroy()
                { _ScopedExLock destroyLock(this);
                    if (destroyLock.getData() != NULL) 
                    {
                        delete destroyLock.getData();
                        destroyLock.setData(NULL);
                    }   
                }

        /**
         *
         */
        bool isValid() const
                { _ScopedShLock valCheckLock(this);                                                            
                    return valCheckLock.getData() != NULL; }

        /**
         *
         */
        GuardedObjectT & operator = (_ClassT * object)
                { assign(object); return *this; }

        //===========================================================================================
        // Lock control methods
        //===========================================================================================

        /**
         *
         */
        _ClassT * exLock()
                { 
                    _ClassT * obj = _exLock(); 
                    DYLAB_ASSERT(obj != NULL); 
					return obj; 
                }

        /**
         *
         */
        const _ClassT * shLock() const
                { 
                    const _ClassT * obj = _shLock(); 
                    DYLAB_ASSERT(obj != NULL); 
					return obj; 
                }

        /**
         *
         */
        void exUnlock()
                { _exUnlock(); }

        /**
         *
         */
        void shUnlock() const
                { _shUnlock(); }
};
}
//===========================================================================================
#endif // __dylab_GuardedObject_h__
